'''

自动求导

torch.autograd是 PyTorch 的自动差分引擎，可为神经网络训练提供支持。 在本节中，您将获得有关 Autograd 如何帮助神经网络训练的概念性理解。

神经网络（NN）是在某些输入数据上执行的嵌套函数的集合。 这些函数由参数（由权重和偏差组成）定义，这些参数在 PyTorch 中存储在张量中。

训练 NN 分为两个步骤：

正向传播：在正向传播中，NN 对正确的输出进行最佳猜测。 它通过其每个函数运行输入数据以进行猜测。

反向传播：在反向传播中，NN 根据其猜测中的误差调整其参数。 它通过从输出向后遍历，收集有关函数参数（梯度）的误差导数并使用梯度下降来优化参数来实现。 有关反向传播的更详细的演练


'''

'''
在 PyTorch 中的用法


让我们来看一个训练步骤。 
对于此示例，我们从torchvision加载了经过预训练的 resnet18 模型。 
我们创建一个随机数据张量来表示具有 3 个通道的单个图像，高度&宽度为 64，其对应的label初始化为一些随机值。


'''
import torch, torchvision
import numpy as np

model = torchvision.models.resnet18(pretrained=True)

data = torch.rand(1, 3, 64, 64)

labels = torch.rand(1, 1000)

print(type(labels))
print(labels.shape)

# 接下来，我们通过模型的每一层运行输入数据以进行预测。 这是正向传播。

prediction = model(data)  # forward pass

# 我们使用模型的预测和相应的标签来计算误差（loss）。
# 下一步是通过网络反向传播此误差。
# 当我们在误差张量上调用.backward()时，开始反向传播。
# 然后，Autograd 会为每个模型参数计算梯度并将其存储在参数的.grad属性中。

loss = (prediction - labels).sum()

loss.backward()
print(loss)

# 接下来，我们加载一个优化器，在本例中为 SGD，学习率为 0.01，动量为 0.9。 我们在优化器中注册模型的所有参数。

optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)

# 最后，我们调用.step()启动梯度下降。 优化器通过.grad中存储的梯度来调整每个参数。
optim.step()


'''

Autograd 的微分

让我们来看看autograd如何收集梯度。 
我们用requires_grad=True创建两个张量a和b。
 这向autograd发出信号，应跟踪对它们的所有操作。
 
'''

a=torch.tensor([2.,3.],requires_grad=True)
b=torch.tensor([6.,4.],requires_grad=True)
print(a)
print(a.shape)


'''
我们从a和b创建另一个张量Q。

Q = 3 * a^3 - b^2
'''

Q = 3*a**3 - b**2

'''
假设a和b是神经网络的参数，Q是误差。 在 NN 训练中，我们想要相对于参数的误差，即

Q 对 a 和 b 求偏导数


当我们在Q上调用.backward()时，Autograd 将计算这些梯度并将其存储在各个张量的.grad属性中。

就是  对 偏导公式  dQa  =  9 * a^2  a=1(初始值)   当 a 的值发生变化   dQa 的值也会变化,  a值变化的公式  a =  a - dQa * r (r 为学习率)

理解 是 a 先有个值 [2,3]    则 dQa =  [36,81]  求导后  a = [2,3] - [36,81] * r(0.01)  即  a =[1.64,2.19]  

下一轮在 求偏导 dQa , 在计算 a的新一轮值 




我们需要在Q.backward()中显式传递gradient参数，因为它是向量。 gradient是与Q形状相同的张量，它表示Q相对于本身的梯度，即


'''
